﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;

namespace Common.Unity
{
	public class CommonExtension : UnityContainerExtension
	{
		internal List<NamedTypeBuildKey> Items
		{
			get { return items ?? ( items = new List<NamedTypeBuildKey>() ); }
		}	List<NamedTypeBuildKey> items;

		internal Dictionary<NamedTypeBuildKey,Type> MappedTypes
		{
			get { return mappedTypes ?? ( mappedTypes = new Dictionary<NamedTypeBuildKey,Type>() ); }
		}	Dictionary<NamedTypeBuildKey,Type> mappedTypes;

		internal Dictionary<NamedTypeBuildKey,List<Type>> Mappings
		{
			get { return mappings ?? ( mappings = new Dictionary<NamedTypeBuildKey,List<Type>>() ); }
		}	Dictionary<NamedTypeBuildKey,List<Type>> mappings;
		
		protected override void Initialize()
		{
			Context.Strategies.AddNew<ObjectCreationStrategy>( UnityBuildStage.PreCreation );

			Context.RegisteringInstance += Context_RegisteringInstance;
			Context.Registering += Context_Registering;
		}

		public override void Remove()
		{
			Context.RegisteringInstance -= Context_RegisteringInstance;
			Context.Registering -= Context_Registering;
		}

		void Context_Registering(object sender, RegisterEventArgs e)
		{
			var key = new NamedTypeBuildKey( e.TypeFrom, e.Name );
			if ( e.TypeTo != null )
			{
				if ( !MappedTypes.ContainsKey( key ) )
				{
					MappedTypes.Add( key, e.TypeTo );
				}
				RegisterMapping( new NamedTypeBuildKey( e.TypeTo, e.Name ), e.TypeFrom );
			}
			Register( key );
		}

		List<Type> ResolveMappingTypes( NamedTypeBuildKey key )
		{
			List<Type> result;
			if ( !Mappings.TryGetValue( key, out result ) )
			{
				Mappings.Add( key, result = new List<Type>() );
				Register( key );
			}
			return result;
		}

		void RegisterMapping( NamedTypeBuildKey key, Type type )
		{
			List<Type> types = ResolveMappingTypes( key );
			if ( !types.Contains( type ) )
			{
				types.Add( type );
			}
		}

		void Register( NamedTypeBuildKey key )
		{
			if ( !Items.Contains( key ) )
			{
				Items.Add( key );
			}
		}

		void Context_RegisteringInstance(object sender, RegisterInstanceEventArgs e)
		{
			Register( new NamedTypeBuildKey( e.RegisteredType, e.Name ) );
		}
	}
}
